home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / ftpserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-08  |  26.7 KB  |  1,059 lines

  1. /* Internet FTP Server server machine - see RFC 959
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  *
  4.  * Mods by KO4KS
  5.  */
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <time.h>
  9. #ifdef    __TURBOC__
  10. #include <io.h>
  11. #include <dir.h>
  12. #endif
  13. #include <sys/stat.h>
  14. #include "global.h"
  15. #include "config.h"
  16. #include "mbuf.h"
  17. #include "socket.h"
  18. #include "ftp.h"
  19. #include "ftpserv.h"
  20. #include "proc.h"
  21. #include "dirutil.h"
  22. #include "files.h"
  23. #include "commands.h"
  24. #include "session.h"
  25. #include "md5.h"
  26. #include "smtp.h"
  27. #ifdef CALLSERVER
  28. #include <string.h>
  29. /* CD-ROM code by Fred Peachman KB7YW */
  30. extern char *CDROM; /* buckbook.c: defines CDROM drive letter e.g. "s:"  */
  31. #endif                  /* #ifdef CALLSERVER  */
  32. #ifdef LZW
  33. #include "lzw.h"
  34. #endif
  35.  
  36. static void ftpserv __ARGS((int s,void *unused,void *p));
  37. static int pport __ARGS((struct sockaddr_in *sock,char *arg));
  38. static void ftplogin __ARGS((struct ftpserv *ftp,char *pass));
  39. static int sendit __ARGS((struct ftpserv *ftp,char *command,char *file));
  40. static int recvit __ARGS((struct ftpserv *ftp,char *command,char *file));
  41. static void sendmsgfile __ARGS((int s,int num,char *buf,int size,FILE *fp));
  42. int doftptdisc __ARGS((int argc, char *argv[], void *p));
  43. extern char *addroot __ARGS((char *root, char *name));
  44. extern int dir_ok __ARGS((char * newpath,struct cur_dirs * dirs));
  45. extern char * defpath __ARGS((struct cur_dirs *curdirs, char *path));
  46.  
  47. /* Command table */
  48. static char *commands[] = {
  49.     "user",
  50.     "acct",
  51.     "pass",
  52.     "type",
  53.     "list",
  54.     "cwd",
  55.     "dele",
  56.     "name",
  57.     "quit",
  58.     "retr",
  59.     "stor",
  60.     "port",
  61.     "nlst",
  62.     "pwd",
  63.     "xpwd",            /* For compatibility with 4.2BSD */
  64.     "mkd ",
  65.     "xmkd",            /* For compatibility with 4.2BSD */
  66.     "xrmd",            /* For compatibility with 4.2BSD */
  67.     "rmd ",
  68.     "stru",
  69.     "mode",
  70.     "syst",
  71.     "xmd5",
  72.     "rsme",                 /* Added by IW0CNB, for resuming interrupted trasnfers */
  73.     "rput",
  74. #ifdef LZW
  75.     "xlzw",
  76. #endif
  77.     "rnfr",
  78.     "rnto",
  79.     "cdup",
  80.     "appe",
  81.     NULLCHAR
  82. };
  83.  
  84. /* Response messages */
  85. #ifdef ITT
  86. static char banner[] = "220 %s, ITT-TMNG FTP version %s\n";
  87. #else
  88. static char banner[] = "220 %s, KA9Q-NOS FTP version %s\n";
  89. #endif
  90. static char banner1[] = "230- Ready on %s";
  91. static char badcmd[] = "500 Unknown command\n";
  92. static char binwarn[] = "100 Warning: type is ASCII and %s appears to be binary\n";
  93. static char unsupp[] = "500 Unsupported command or option\n";
  94. static char givepass[] = "331 Enter PASS command\n";
  95. static char anonokay[] = "331 Anonymous access, give email address as password\n";
  96. static char challenge[] = "399 PASS challenge : %016lx\n";
  97. static char logged[] = "230 Logged in\n";
  98. static char loggeda[] = "230 Logged in as anonymous, restrictions apply\n";
  99. static char typeok[] = "200 Type %s OK\n";
  100. static char only8[] = "501 Only logical bytesize 8 supported\n";
  101. static char deleok[] = "250 File deleted\n";
  102. static char mkdok[] = "200 MKD ok\n";
  103. static char delefail[] = "550 Delete failed: %s\n";
  104. static char pwdmsg[] = "257 \"%s\" is current directory\n";
  105. static char badtype[] = "501 Unknown type \"%s\"\n";
  106. static char badport[] = "501 Bad port syntax\n";
  107. static char unimp[] = "502 Command not yet implemented\n";
  108. static char bye[] = "221 Goodbye!\n";
  109. static char nodir[] = "553 Can't read directory \"%s\": %s\n";
  110. static char cantopen[] = "550 Can't read file \"%s\": %s\n";
  111. static char sending[] = "150 Opening data connection for %s %s %s\n"; /*N1BEE*/
  112. static char cantmake[] = "553 Can't create \"%s\": %s\n";
  113. static char writerr[] = "552 Write error: %s\n";
  114. static char portok[] = "200 Port command okay\n";
  115. static char rxok[] = "226 File received OK\n";
  116. static char txok[] = "226 File sent OK\n";
  117. static char noperm[] = "550 Permission denied\n";
  118. static char noconn[] = "425 Data connection reset\n";
  119. static char badcheck[] = "425 Bad checksum\n";
  120. static char lowmem[] = "421 System overloaded, try again later\n";
  121. static char notlog[] = "530 Please log in with USER and PASS\n";
  122. static char userfirst[] = "503 Login with USER first.\n";
  123. static char okay[] = "200 Ok\n";
  124. static char syst[] = "215 %s Type: L%d Version: %s\n";
  125. static char pendingto[] = "350 Rename awaiting new name.\n";
  126. static char badseq[] = "503 No prior RNFR received - RNTO ignored\n";
  127. static char norename[] = "550 Can't rename: %s\n";
  128.  
  129. int Sftp = -1;    /* Prototype socket for service */
  130. int FtpUsers = 0;
  131.  
  132. #ifdef FTPTDISC
  133. int32 Ftptdiscinit = 0;
  134.  
  135. /* Set ftp redundancy timer */
  136. int
  137. doftptdisc(argc,argv,p)
  138. int argc;
  139. char *argv[];
  140. void *p;
  141. {
  142.     return setlong(&Ftptdiscinit,"Ftp redundancy timer (sec)",argc,argv);
  143. }
  144.  
  145. static void
  146. ftp_redundant(ftp)
  147. struct ftpserv *ftp;
  148. {
  149.     /* Clean up */
  150.     shutdown(ftp->control,2);
  151.     close_s(ftp->control);
  152.     if(ftp->data != -1){
  153.         shutdown(ftp->data,2);
  154.         close_s(ftp->data);
  155.         ftp->data = -1;
  156.     }
  157.     return;
  158. }
  159. #endif
  160.  
  161. /* Start up FTP service */
  162. int
  163. ftpstart(argc,argv,p)
  164. int argc;
  165. char *argv[];
  166. void *p;
  167. {
  168.     return (installserver (argc, argv, &Sftp, "FTP listener", IPPORT_FTP,
  169.         "ftpserv", ftpserv, 2048, NULL));
  170. }
  171.  
  172. static void sendmsgfile(s, num, buf, size, fp)
  173. int s, num;
  174. char *buf;
  175. int size;
  176. FILE *fp;
  177. {
  178.  
  179.     while(fgets(buf,size,fp)) {
  180.         rip(buf);
  181.         usprintf(s,"%d- %s\n",num,buf);
  182.     }
  183. }
  184.  
  185. static int
  186. isanonymous (name)
  187. char *name;
  188. {
  189. FILE *fp;
  190. int retval = 1;
  191. char buf[128], *cp;
  192.  
  193.     if((fp = fopen(Userfile,READ_TEXT)) != NULLFILE)    {
  194.         while ( fgets(buf,128,fp) != NULLCHAR ){
  195.             if((cp = strpbrk(buf," \t")) == NULLCHAR)
  196.                 /* Bogus entry */
  197.                 continue;
  198.             *cp++ = '\0';
  199.  
  200.             if(!stricmp(name,buf))    {
  201.                 retval = 0;
  202.                 break;        /* Found user */
  203.             }
  204.         }
  205.         fclose (fp);
  206.     }
  207.     return (retval);
  208. }
  209.  
  210.  
  211. static void
  212. ftpserv(s,unused,p)
  213. int s;    /* Socket with user connection */
  214. void *unused;
  215. void *p;
  216. {
  217.     struct ftpserv ftp;
  218.     struct stat cwdstat;
  219.     char **cmdp,buf[512],*arg,*cp,*cp1,*file,*mode;
  220.     long t;
  221.     int cnt,i;
  222.     struct sockaddr_in socket;
  223.     struct  cur_dirs dirs;
  224.     char *rnfrom = NULLCHAR;
  225.     FILE *fpm;
  226.  
  227.     sockmode(s,SOCK_ASCII);
  228.     memset((char *)&ftp,0,sizeof(ftp));    /* Start with clear slate */
  229.     ftp.data = -1;
  230.  
  231.     sockowner(s,Curproc);        /* We own it now */
  232.     ftp.control = s;
  233.     /* Set default data port */
  234.     i = SOCKSIZE;
  235.     getpeername(s,(char *)&socket,&i);
  236.     socket.sin_port = IPPORT_FTPD;
  237.     ASSIGN(ftp.port,socket);
  238.  
  239. #ifdef FTPTDISC
  240.     /* Set the timeout timer - WG7J */
  241.     set_timer(&ftp.tdisc,Ftptdiscinit * 1000L);
  242.     ftp.tdisc.func = ftp_redundant;
  243.     ftp.tdisc.arg = &ftp;
  244.     start_timer(&ftp.tdisc);
  245. #endif
  246.  
  247.     log(s,"open FTP");
  248.     FtpUsers++;
  249.     init_dirs(&dirs);
  250.     ftp.curdirs = &dirs;      
  251.     usprintf(s,banner,Hostname,Version);
  252.     time(&t);
  253.     cp = ctime(&t);
  254. #ifdef notdef
  255.     if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  256.         *cp1 = '\0';
  257. #endif
  258.  
  259.     /* Command interpreting loop */
  260. loop:
  261.     if((cnt = recvline(s,buf,sizeof(buf))) == -1){
  262.         /* He closed on us */
  263.         goto finish;
  264.     }
  265. #ifdef FTPTDISC
  266.     /* Reset the timeout timer - WG7J */
  267.     start_timer(&ftp.tdisc);
  268. #endif
  269.     if(cnt == 0){
  270.         /* Can't be a legal FTP command */
  271.         usprintf(ftp.control,badcmd);
  272.         goto loop;
  273.     }    
  274.     rip(buf);
  275.     /* Translate first word to lower case */
  276.     for(cp = buf;*cp != ' ' && *cp != '\0';cp++)
  277.         *cp = tolower(*cp);
  278.     /* Find command in table; if not present, return syntax error */
  279.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  280.         if(strnicmp(*cmdp,buf,strlen(*cmdp)) == 0)
  281.             break;
  282.     if(*cmdp == NULLCHAR){
  283.         usprintf(ftp.control,badcmd);
  284.         goto loop;
  285.     }
  286.     /* Allow only USER, PASS and QUIT before logging in */
  287.     if(ftp.path == NULLCHAR){
  288.         switch(cmdp-commands){
  289.         case USER_CMD:
  290.         case PASS_CMD:
  291.         case QUIT_CMD:
  292.             break;
  293.         default:
  294.             usprintf(ftp.control,notlog);
  295.             goto loop;
  296.         }
  297.     }
  298.     arg = &buf[strlen(*cmdp)];
  299.     while(*arg == ' ')
  300.         arg++;
  301.  
  302.     /* Execute specific command */
  303.     switch(cmdp-commands){
  304.     case USER_CMD:
  305.         free(ftp.username);
  306.         ftp.username = strdup(arg);
  307.          /*   if(sp_user(ftp.username)) {
  308.             time(&ftp.ttim);
  309.             usprintf(ftp.control,challenge,ftp.ttim);
  310.         }
  311.         else
  312.          */
  313.             if (isanonymous (arg))
  314.             usprintf(ftp.control,anonokay);
  315.             else
  316.             usprintf(ftp.control,givepass);
  317.         break;
  318.     case TYPE_CMD:
  319.         switch(arg[0]){
  320.         case 'A':
  321.         case 'a':    /* Ascii */
  322.             ftp.type = ASCII_TYPE;
  323.             usprintf(ftp.control,typeok,arg);
  324.             break;
  325.         case 'l':
  326.         case 'L':
  327.             while(*arg != ' ' && *arg != '\0')
  328.                 arg++;
  329.             if(*arg == '\0' || *++arg != '8'){
  330.                 usprintf(ftp.control,only8);
  331.                 break;
  332.             }
  333.             ftp.type = LOGICAL_TYPE;
  334.             ftp.logbsize = 8;
  335.             usprintf(ftp.control,typeok,arg);
  336.             break;
  337.         case 'B':
  338.         case 'b':    /* Binary */
  339.         case 'I':
  340.         case 'i':    /* Image */
  341.             ftp.type = IMAGE_TYPE;
  342.             usprintf(ftp.control,typeok,arg);
  343.             break;
  344.         default:    /* Invalid */
  345.             usprintf(ftp.control,badtype,arg);
  346.             break;
  347.         }
  348.         break;
  349.     case QUIT_CMD:
  350.         usprintf(ftp.control,bye);
  351.         goto finish;
  352.     case RNFR_CMD:
  353.         file = addroot(ftp.curdirs->dir,arg);
  354.         if(!permcheck(ftp.path,ftp.perms,RNFR_CMD,file))
  355.              usprintf(ftp.control,noperm);
  356.         else if (access (file, 6))
  357.             usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
  358.         else    {
  359.             usprintf(ftp.control,pendingto);
  360.             rnfrom = strdup (file);
  361.         }
  362.         free (file);
  363.         break;
  364.     case RNTO_CMD:
  365.         file = addroot(ftp.curdirs->dir,arg);
  366.         if(rnfrom == NULLCHAR)
  367.             usprintf(ftp.control,badseq);
  368.         else    {
  369.             if(!permcheck(ftp.path,ftp.perms,RNTO_CMD,file))
  370.                  usprintf(ftp.control,noperm);
  371.             else    {
  372.                 if(rename(rnfrom,file) == -1)
  373.                     usprintf(ftp.control,norename,sys_errlist[errno]);
  374.                 else
  375.                     usprintf(ftp.control,okay);
  376.             }
  377.             free (rnfrom);
  378.             rnfrom = NULLCHAR;
  379.         }
  380.         free (file);
  381.         break;
  382.     case RETR_CMD:
  383.     case RSME_CMD:
  384.         file = addroot(ftp.curdirs->dir,arg);
  385.         switch(ftp.type){
  386.         case IMAGE_TYPE:
  387.         case LOGICAL_TYPE:
  388.             mode = READ_BINARY;
  389.             break;
  390.         case ASCII_TYPE:
  391.             mode = READ_TEXT;
  392.             break;
  393.         }
  394.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  395.              usprintf(ftp.control,noperm);
  396.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  397.             usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
  398.         } else {
  399.             if((cmdp-commands) == RSME_CMD) {
  400.                 log(ftp.control,"RSME %s",file);
  401.                 if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
  402.                     usprintf(ftp.control,binwarn,file);
  403.                 }
  404.                 sendit(&ftp,"RSME",file);
  405.             } else {
  406.                 log(ftp.control,"RETR %s",file);
  407.                 if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
  408.                     usprintf(ftp.control,binwarn,file);
  409.                 }
  410.                 sendit(&ftp,"RETR",file);
  411.             }    
  412.         }
  413.         free(file);
  414.         break;
  415.     case STOR_CMD:
  416.         cp = "STOR";
  417.         goto store2;
  418.     case APPE_CMD:
  419.         cp = "APPE";
  420.         goto store2;
  421.     case RPUT_CMD:
  422.         cp = "RPUT";
  423. store2:        file = addroot(ftp.curdirs->dir,arg);
  424.         switch(ftp.type){
  425.         case IMAGE_TYPE:
  426.         case LOGICAL_TYPE:
  427.             if(cmdp-commands != STOR_CMD)
  428.                 mode = APPEND_BINARY;
  429.             else
  430.                 mode = WRITE_BINARY;
  431.             break;
  432.         case ASCII_TYPE:
  433.             if(cmdp-commands != STOR_CMD)
  434.                 mode = APPEND_TEXT;
  435.             else
  436.                 mode = WRITE_TEXT;
  437.             break;
  438.         }
  439.         if(!permcheck(ftp.path,ftp.perms,cmdp-commands,file)){
  440.             usprintf(ftp.control,noperm);
  441.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  442.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  443.         } else {
  444.             log(ftp.control,"%s %s",cp, file);
  445.             recvit(&ftp,cp,file);
  446.         }
  447.         free(file);
  448.         break;
  449.     case PORT_CMD:
  450.         if(pport(&ftp.port,arg) == -1){
  451.             usprintf(ftp.control,badport);
  452.         } else {
  453.             usprintf(ftp.control,portok);
  454.         }
  455.         break;
  456. #ifndef CPM
  457.     case LIST_CMD:
  458.     case NLST_CMD:
  459.         file = addroot(ftp.curdirs->dir,defpath (ftp.curdirs, arg));
  460.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file))
  461.              usprintf(ftp.control,noperm);
  462.         else     {
  463.             cp = Command->curdirs->dir;
  464.             Command->curdirs->dir = ftp.curdirs->dir;
  465.             if((ftp.fp = dir(file,((cmdp-commands) == LIST_CMD) ? 1 : 0)) == NULLFILE)
  466.                 usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  467.             else
  468.                 sendit(&ftp,((cmdp-commands) == LIST_CMD) ? "LIST" : "NLST",file);
  469.             Command->curdirs->dir = cp;
  470.         }
  471.         free(file);
  472.         break;
  473.     case CDUP_CMD:
  474.         sprintf (arg, "..");    /* and fall through */
  475.     case CWD_CMD:
  476. cwdentry:
  477. #ifdef old_CALLSERVER
  478.         /* if the requested path contains the CROM drive letter:  */
  479.         if (CDROM != NULLCHAR && strnicmp(CDROM, arg, 2) == 0)  {
  480.             if (strchr(arg, '/') == NULLCHAR) {
  481.                 file = (char *)malloc(strlen(arg) + 2);
  482.                 sprintf(file,"%s/", arg);
  483.             } else  file = strdup(arg);
  484.             if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  485.                 usprintf(ftp.control,noperm);
  486.                 free(file);
  487.             /* Don'tcha just LOVE %%$#@!! MS-DOS? - which is what we are running */
  488.             } else if(file[2] == '/' || access(file,0) == 0){
  489.                 /* Succeeded, record in control block */
  490.                 free(ftp.cd);
  491.                 ftp.cd = file;
  492.                 usprintf(ftp.control,"You may return to your default drive & directory by entering:\n\t\t\"cd %s\"\n\n", ftp.path);
  493.                 usprintf(ftp.control,pwdmsg,file);
  494.             } else {
  495.                 /* Failed, don't change anything */
  496.                 usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  497.                 free(file);
  498.             }
  499.             break;
  500.         }
  501.         /* requested path does not contain CDROM drive letter:                      */
  502.         /* if current dir is in CDROM - and a "off-root" is requested:
  503.            go back to default path.        */
  504.         if ((CDROM != NULLCHAR && strnicmp(ftp.cd, CDROM, 2) == 0) && (arg[0] == '/'))  {
  505.             free(ftp.cd);
  506.             ftp.cd = strdup(ftp.path);      /* go back to default path            */
  507.         }
  508. #endif  /* #ifdef CALLSERVER  */
  509.         file = addroot(ftp.curdirs->dir,arg);
  510.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file))
  511.              usprintf(ftp.control,noperm);
  512.         else if (dir_ok(file,ftp.curdirs))    {
  513.             /* Succeeded */
  514.                 /* If exists, send the contents of 'desc.ftp' in the new
  515.                  * directory...
  516.                  */
  517.                 strcpy(buf,file);
  518.                 if((fpm = fopen(strcat(buf,"/desc.ftp"),"r")) != NULL) {
  519.                     sendmsgfile(ftp.control,257,buf,sizeof(buf),fpm);
  520.                     fclose(fpm);
  521.                 }
  522.  
  523.             usprintf(ftp.control,pwdmsg,ftp.curdirs->dir);
  524.         } else
  525.             /* Failed, nothing changed */
  526.             usprintf(ftp.control,nodir,arg,sys_errlist[errno]);
  527.         free(file);
  528.         break;
  529.     case XPWD_CMD:
  530.     case PWD_CMD:
  531.         usprintf(ftp.control,pwdmsg,ftp.curdirs->dir);
  532.         break;
  533. #else
  534.     case LIST_CMD:
  535.     case NLST_CMD:
  536.     case CWD_CMD:
  537.     case XPWD_CMD:
  538.     case PWD_CMD:
  539. #endif
  540.     case ACCT_CMD:        
  541.         usprintf(ftp.control,unimp);
  542.         break;
  543.     case DELE_CMD:
  544.         file = addroot(ftp.curdirs->dir,arg);
  545.         if(!permcheck(ftp.path,ftp.perms,DELE_CMD,file)){
  546.              usprintf(ftp.control,noperm);
  547.         } else if(unlink(file) == 0){
  548.             log(ftp.control,"DELE %s",file);
  549.             usprintf(ftp.control,deleok);
  550.         } else {
  551.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  552.         }
  553.         free(file);
  554.         break;
  555.     case PASS_CMD:
  556.         if(ftp.username == NULLCHAR)
  557.             usprintf(ftp.control,userfirst);
  558.         else
  559.             ftplogin(&ftp,arg);
  560.         break;
  561. #ifndef    CPM
  562.     case XMKD_CMD:
  563.     case MKD_CMD:
  564.         file = addroot(ftp.curdirs->dir,arg);
  565.         if(!permcheck(ftp.path,ftp.perms,MKD_CMD,file)){
  566.             usprintf(ftp.control,noperm);
  567. #ifdef    UNIX
  568.         } else if(mkdir(file,0777) == 0){
  569. #else
  570.         } else if(mkdir(file) == 0){
  571. #endif
  572.             log(ftp.control,"MKD %s",file);
  573.             usprintf(ftp.control,mkdok);
  574.         } else {
  575.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  576.         }
  577.         free(file);
  578.         break;
  579.     case XRMD_CMD:
  580.     case RMD_CMD:
  581.         file = addroot(ftp.curdirs->dir,arg);
  582.         if(!permcheck(ftp.path,ftp.perms,RMD_CMD,file)){
  583.              usprintf(ftp.control,noperm);
  584.         } else if(rmdir(file) == 0){
  585.             log(ftp.control,"RMD %s",file);
  586.             usprintf(ftp.control,deleok);
  587.         } else {
  588.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  589.         }
  590.         free(file);
  591.         break;
  592.     case STRU_CMD:
  593.         if(tolower(arg[0]) != 'f')
  594.             usprintf(ftp.control,unsupp);
  595.         else
  596.             usprintf(ftp.control,okay);
  597.         break;
  598.     case MODE_CMD:
  599.         if(tolower(arg[0]) != 's')
  600.             usprintf(ftp.control,unsupp);
  601.         else
  602.             usprintf(ftp.control,okay);
  603.         break;
  604.     case SYST_CMD:
  605.         usprintf(ftp.control,syst,System,NBBY,Version);
  606.         break;
  607.     case XMD5_CMD:
  608.         file = addroot(ftp.curdirs->dir,arg);
  609.         switch(ftp.type){
  610.         case IMAGE_TYPE:
  611.         case LOGICAL_TYPE:
  612.             mode = READ_BINARY;
  613.             break;
  614.         case ASCII_TYPE:
  615.             mode = READ_TEXT;
  616.             break;
  617.         }
  618.         if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
  619.              usprintf(ftp.control,noperm);
  620.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  621.             usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
  622.         } else {
  623.             char hash[16];
  624.  
  625.             log(ftp.control,"XMD5 %s",file);
  626.             if(ftp.type == ASCII_TYPE && isbinary(ftp.fp))
  627.                 usprintf(ftp.control,binwarn,file);
  628.  
  629.             md5hash(ftp.fp,hash,ftp.type == ASCII_TYPE);
  630.             fclose(ftp.fp);
  631.             ftp.fp = NULLFILE;
  632.             usprintf(ftp.control,"200 ");
  633.             for(i=0;i<16;i++)
  634.                 usprintf(ftp.control,"%02x",hash[i] & 0xff);
  635.             usprintf(ftp.control," %s\n",file);
  636.         }
  637.         free(file);
  638.         break;
  639. #ifdef LZW
  640.     case XLZW_CMD:
  641.         if (ftp.lzw)
  642.             usprintf (ftp.control, "550 Already using LZW compression\n");
  643.         else    {
  644.             int lzwbits, lzwmode;
  645.             usprintf (ftp.control, okay);
  646.             sscanf(&buf[5],"%d %d",&ftp.lzwbits,&ftp.lzwmode);
  647. /*            lzwinit (ftp.control, ftp.lzwbits, ftp.lzwmode);    */
  648.             ftp.lzw = 1;
  649.         }
  650.         break;
  651. #endif
  652.     }
  653. #endif
  654.     goto loop;
  655. finish:
  656.  
  657. #ifdef FTPTDISC
  658.     stop_timer(&ftp.tdisc);
  659. #endif
  660.  
  661.     log(ftp.control,"close FTP from '%s'", ftp.username);
  662.     FtpUsers--;
  663.     /* Clean up */
  664.     close_s(ftp.control);
  665.     if(ftp.data != -1)
  666.         close_s(ftp.data);
  667.     if(ftp.fp != NULLFILE)
  668.         fclose(ftp.fp);
  669.     free(ftp.username);
  670.     free(ftp.path);
  671.     free_dirs(&dirs);
  672.     free(rnfrom);
  673. }
  674.  
  675. /* Shut down FTP server */
  676. int
  677. ftp0(argc,argv,p)
  678. int argc;
  679. char *argv[];
  680. void *p;
  681. {
  682.     return (deleteserver (&Sftp));
  683. }
  684. static
  685. int
  686. pport(sock,arg)
  687. struct sockaddr_in *sock;
  688. char *arg;
  689. {
  690.     int32 n;
  691.     int i;
  692.  
  693.     n = 0;
  694.     for(i=0;i<4;i++){
  695.         n = atoi(arg) + (n << 8);
  696.         if((arg = strchr(arg,',')) == NULLCHAR)
  697.             return -1;
  698.         arg++;
  699.     }
  700.     sock->sin_addr.s_addr = n;
  701.     n = atoi(arg);
  702.     if((arg = strchr(arg,',')) == NULLCHAR)
  703.         return -1;
  704.     arg++;
  705.     n = atoi(arg) + (n << 8);
  706.     sock->sin_port = n;
  707.     return 0;
  708. }
  709.  
  710. /* Attempt to log in the user whose name is in ftp->username and password
  711.  * in pass
  712.  */
  713. static void
  714. ftplogin(ftp,pass)
  715. struct ftpserv *ftp;
  716. char *pass;
  717. {
  718.     char *path, buf[128], *cp;
  719.     char *p;
  720.     long t;
  721.     FILE *fp;
  722.     int anony = 0;
  723.  
  724.     path = mallocw(200);
  725.     if((ftp->perms = userlogin(ftp->username,pass,&path,200,&anony))
  726.        == -1){
  727.         log(ftp->control,"FTP login refused - '%s'", ftp->username);
  728.         usprintf(ftp->control,noperm);
  729.         free(path);
  730.         return;
  731.     }
  732.     /* Set up current directory and path prefix */
  733. #if    defined(AMIGAGONE)
  734.     ftp->path = strdup(ftp->cd);
  735.     free(path);
  736. #else
  737.     ftp->path = path;
  738. #endif
  739.  
  740.     {
  741.     FILE *out;
  742.         sprintf(buf,"%s/ftp.log",Spool);
  743.         if((out = fopen(buf,APPEND_TEXT)) != NULLFILE)    {
  744.             time_t t;
  745.             time(&t);
  746.             fprintf (out, "FTP from %s (%s) on %s", ftp->username, pass, ptime(&t));
  747.             fclose (out);
  748.         }
  749.     }
  750.  
  751.        time(&t);
  752.        cp = ctime(&t);
  753.        usprintf(ftp->control,banner1,cp);
  754.  
  755.     /* everyone gets the ftpmotd file, if it exists */
  756.      if((fp = fopen(Ftpmotd,"r")) != NULL) {
  757.      sendmsgfile(ftp->control,230,buf,sizeof(buf),fp);
  758.          fclose(fp);
  759.      }
  760.  
  761.     /* if there is a message.ftp file in the login dir, send it too */
  762.     sprintf(buf,"%s%s%s", path, (path[strlen(path)-1] == '/') ? "" : "/", "message.ftp");
  763.     if((fp = fopen(buf,"r")) != NULL) {
  764.         sendmsgfile(ftp->control,230,buf,sizeof(buf),fp);
  765.         fclose(fp);
  766.     }
  767.  
  768.     path = strdup (ftp->path);
  769.     if ((p = strpbrk (path, "=;")) != NULLCHAR)
  770.         *p = 0;
  771.     if (dir_ok(path,ftp->curdirs))        {
  772.         /* Succeeded */
  773.         /* If exists, send the contents of 'desc.ftp' in the new directory... */
  774.         sprintf (buf, "%s/desc.ftp", path);
  775.         if((fp = fopen(buf,"r")) != NULL) {
  776.                     sendmsgfile(ftp->control,230,buf,sizeof(buf),fp);
  777.                        fclose(fp);
  778.         }
  779.     }
  780.  
  781.     free (path);
  782.     if(!anony){
  783.         usprintf(ftp->control,logged);
  784.         log(ftp->control,"FTP login - '%s'",ftp->username);
  785.     } else {
  786.         usprintf(ftp->control,loggeda);
  787.         log(ftp->control,"FTP anonymous login - '%s' (%s)",ftp->username,pass);
  788.     }
  789.     usflush (ftp->control);
  790.     pwait (NULL);
  791. }
  792.  
  793. #ifdef    MSDOS
  794. /* Illegal characters in a DOS filename */
  795. static char badchars[] = "\"[]|<>+=;,";
  796. #endif
  797.  
  798. /* Return 1 if the file operation is allowed, 0 otherwise */
  799. int
  800. permcheck(path,perms,op,file)
  801. char *path;
  802. int perms;
  803. int op;
  804. char *file;
  805. {
  806.     char *cp, *cp1;
  807.     int newperms;
  808.  
  809.     if(file == NULLCHAR || path == NULLCHAR)
  810.         return 0;    /* Probably hasn't logged in yet */
  811.  
  812. /* To get to the CDROM - EVERYBODY gets read privs, regardless of what
  813.   /ftpusers has to say about it!!! - kb7yw */
  814.  
  815. #ifdef CALLSERVER
  816.       if (CDROM != NULLCHAR && strnicmp(file, CDROM ,2) == 0) {
  817.       /* Check for characters illegal in MS-DOS file names */
  818.       for(cp = badchars;*cp != '\0';cp++){
  819.         if(strchr(&file[2],*cp) != NULLCHAR)
  820.         return 0;
  821.       }
  822.  
  823.         switch(op){ /* What to do when the user is on the cd-rom drive      */
  824.         case RETR_CMD: /* Everybody gets read privs regardless of ftpusers  */
  825.           /* User has permission to read files */
  826.             return 1;
  827.         case DELE_CMD:
  828.         case RMD_CMD:
  829.           /* User must not have permission to (over)write files */
  830.         case STOR_CMD:
  831.         case MKD_CMD:
  832.           /* User must NOT have permission to (over)write files
  833.            */
  834.           return 0;
  835.         }   /* switch(op) */
  836.       }     /* if strncmp(....  */
  837. #endif    /* #ifdef CALLSERVER  */
  838. #ifndef MAC
  839.     /* The target file must be under the user's allowed search path */
  840.     /* We let them specify multiple paths using path;path... -russ */
  841.     for(cp = path;;cp = cp1+1){
  842.         char *cp2;
  843.         newperms = perms;
  844.         if((cp1 = strchr(cp,';')) == NULLCHAR)    cp1 = strchr(cp,'\0');
  845.         if (((cp2 = strchr(cp, '=')) == NULLCHAR) || (cp2 > cp1))
  846.             cp2 = cp1;
  847.         else
  848.             newperms = atoi (cp2+1);
  849.         if(!strnicmp(file,cp,cp2 - cp)
  850. #ifdef MSDOS    /* start past the X: drive letter */
  851.             || !strnicmp(file+2,cp,cp2 - cp)
  852. #endif
  853.             )    {
  854.             perms = newperms;
  855.             break;
  856.         }
  857.         if(*cp1 == '\0')    return 0;
  858.     }
  859. #endif
  860.  
  861. #ifdef    MSDOS
  862.     /* Check for characters illegal in MS-DOS file names */
  863.     for(cp = badchars;*cp != '\0';cp++){
  864.         if(strchr(file,*cp) != NULLCHAR)
  865.             return 0;    
  866.     }
  867. #endif
  868.  
  869.     switch(op){
  870.     case RNFR_CMD:
  871.     case RNTO_CMD:
  872.     case RETR_CMD:
  873.         /* User must have permission to read files */
  874.         if(perms & FTP_READ)
  875.             return 1;
  876.         return 0;
  877.     case DELE_CMD:
  878.     case RMD_CMD:
  879.     case RPUT_CMD:
  880.     case APPE_CMD:
  881.         /* User must have permission to (over)write files */
  882.         if(perms & FTP_WRITE)
  883.             return 1;
  884.         return 0;
  885.     case STOR_CMD:
  886.     case MKD_CMD:
  887.         /* User must have permission to (over)write files, or permission
  888.          * to create them if the file doesn't already exist
  889.          */
  890.         if(perms & FTP_WRITE)
  891.             return 1;
  892.         if(access(file,2) == -1 && (perms & FTP_CREATE))
  893.             return 1;
  894.         return 0;
  895.     }
  896.     return 0;    /* "can't happen" -- keep lint happy */
  897. }
  898. static int
  899. sendit(ftp,command,file)
  900. struct ftpserv *ftp;
  901. char *command;
  902. char *file;
  903. {
  904.     long total, starting;
  905.     unsigned long check;
  906.     struct sockaddr_in dport;
  907.     char *cp;
  908.     char fsizetext[20];                /* N1BEE */
  909.  
  910.     fsizetext[0] = 0;
  911.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  912.     dport.sin_family = AF_INET;
  913.     dport.sin_addr.s_addr = INADDR_ANY;
  914.     dport.sin_port = IPPORT_FTPD;
  915.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  916.     sprintf(fsizetext, "(%lu bytes)", filelength(fileno(ftp->fp)));
  917.     usprintf(ftp->control,sending,command,file,fsizetext);  /* N1BEE */
  918.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  919.         fclose(ftp->fp);
  920.         ftp->fp = NULLFILE;
  921.         close_s(ftp->data);
  922.         ftp->data = -1;
  923.         usprintf(ftp->control,noconn);
  924.         return -1;
  925.     }
  926.     if(strcmp(command,"RSME") == 0) {
  927.         cp = mallocw(40);
  928.         recvline(ftp->control,cp,40);
  929.         starting = atol(cp);
  930.         /* If checksum field is not present go on anyway, for compatibility
  931.          * with previous scheme. If present check it and barf if wrong.
  932.          */
  933.         if(strchr(cp,' ') != NULL){
  934.             check = (unsigned long)atol(strchr(cp,' '));
  935.             check -= checksum(ftp->fp,starting);
  936.             if(check != 0){
  937.                 free(cp);
  938.                 usprintf(ftp->control,badcheck);
  939.                 shutdown(ftp->data,1);  /* Blow away data connection */
  940.                 goto send_err;
  941.             }
  942.         } else if(fseek(ftp->fp,starting,SEEK_SET) != 0) {
  943.             free(cp);
  944.             usprintf(ftp->control,noconn);
  945.             shutdown(ftp->data,2);  /* Blow away data connection */
  946.             goto send_err;
  947.         }
  948.     }
  949.  
  950. #ifdef FTPTDISC
  951.     /* Turn off the timeout timer here, some ftp's could
  952.      * take a long time with sloooow packet channels - WG7J
  953.      */
  954.     stop_timer(&ftp->tdisc);
  955. #endif
  956.     
  957. #ifdef LZW
  958.     if (ftp->lzw)
  959.         lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);    
  960. #endif
  961.  
  962.     /* Do the actual transfer */
  963.     total = sendfile(ftp->fp,ftp->data,ftp->type,0);
  964.  
  965. #ifdef FTPTDISC
  966.     /* And turn it back on now */
  967.     start_timer(&ftp->tdisc);
  968. #endif
  969.  
  970.     if(total == -1){
  971.         /* An error occurred on the data connection */
  972.         usprintf(ftp->control,noconn);
  973.         shutdown(ftp->data,2);  /* Blow away data connection */
  974.     } else {
  975.         usprintf(ftp->control,txok);
  976.     }
  977. send_err:       fclose(ftp->fp);
  978.     ftp->fp = NULLFILE;
  979.     close_s(ftp->data);
  980.     ftp->data = -1;
  981.     if(total == -1)
  982.         return -1;
  983.     else
  984.         return 0;
  985. }
  986. static int
  987. recvit(ftp,command,file)
  988. struct ftpserv *ftp;
  989. char *command;
  990. char *file;
  991. {
  992.     struct sockaddr_in dport;
  993.     long total, starting;
  994.  
  995.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  996.     dport.sin_family = AF_INET;
  997.     dport.sin_addr.s_addr = INADDR_ANY;
  998.     dport.sin_port = IPPORT_FTPD;
  999.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  1000.     usprintf(ftp->control,sending,command,file,"");
  1001.  
  1002.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  1003.         fclose(ftp->fp);
  1004.         ftp->fp = NULLFILE;
  1005.         close_s(ftp->data);
  1006.         ftp->data = -1;
  1007.         usprintf(ftp->control,noconn);
  1008.         return -1;
  1009.     }
  1010.     if (strcmp(command, "APPE") == 0)
  1011.         fseek(ftp->fp,0,SEEK_END);
  1012.     if(strcmp(command,"RPUT") == 0){
  1013.         if((starting = getsize(ftp->fp)) == -1)
  1014.             starting = 0L;
  1015.         usprintf(ftp->control,"%lu %lu\n",starting,checksum(ftp->fp,starting));
  1016.         fseek(ftp->fp,starting,SEEK_SET);
  1017.     }
  1018.  
  1019. #ifdef FTPTDISC
  1020.     /* Turn of the timeout timer here; some ftp's could
  1021.      * take a long time with sloooow packet channels - WG7J
  1022.      */
  1023.     stop_timer(&ftp->tdisc);
  1024. #endif
  1025.  
  1026. #ifdef LZW
  1027.     if (ftp->lzw)
  1028.         lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);    
  1029. #endif
  1030.  
  1031.     /* Do the actual transfer */
  1032.     total = recvfile(ftp->fp,ftp->data,ftp->type,0);
  1033.  
  1034. #ifdef FTPTDISC
  1035.     /* And turn it back on now */
  1036.     start_timer(&ftp->tdisc);
  1037. #endif
  1038.  
  1039. #ifdef    CPM
  1040.     if(ftp->type == ASCII_TYPE)
  1041.         putc(CTLZ,ftp->fp);
  1042. #endif
  1043.     if(total == -1) {
  1044.         /* An error occurred while writing the file */
  1045.         usprintf(ftp->control,writerr,sys_errlist[errno]);
  1046.         shutdown(ftp->data,2);    /* Blow it away */
  1047.     } else {
  1048.         usprintf(ftp->control,rxok);
  1049.         close_s(ftp->data);
  1050.     }
  1051.     ftp->data = -1;
  1052.     fclose(ftp->fp);
  1053.     ftp->fp = NULLFILE;
  1054.     if(total == -1)
  1055.         return -1;
  1056.     else
  1057.         return 0;
  1058. }
  1059.